#pragma once
#include "../source/message.hpp"
#include "../source/net.hpp"
#include <set>
namespace ns_server_registry
{
    class ProviderManager
    {
    public:
        using ptr = std::shared_ptr<ProviderManager>;
        struct Provider
        {
            using ptr = std::shared_ptr<Provider>;
            std::mutex _mutex;
            BaseConnection::ptr _conn;
            Address _host;
            std::vector<std::string> _methods;
            Provider(const BaseConnection::ptr &conn, const Address &host)
                : _conn(conn), _host(host)
            {
            }
            void AppendMethod(const std::string &method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _methods.emplace_back(method);
            }
        };
        // 服务提供者的新服务上线
        Provider::ptr AddProvider(const BaseConnection::ptr &conn, const Address &host, const std::string method)
        {
            Provider::ptr provider;
            // 1.查找连接所关联的服务提供者的信息，有则获取，没有则创建
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _hashconns.find(conn);
                if (it != _hashconns.end())
                    provider = it->second;
                else
                {
                    provider = std::make_shared<Provider>(conn, host);
                    _hashconns[conn] = provider;
                }
                // 2.把服务提供者和它提供的方法的关联关系保存进_hashproviders
                // 拿到_hashproviders里，服务提供者提供的方法对应的所有服务提供者，把新的提供者加进去
                auto &providers = _hashproviders[method];
                providers.insert(provider);
            }
            // 给对应服务提供者新增一个能提供的服务名称
            provider->AppendMethod(method);
        }
        // 服务提供者连接断开时，获取它的信息————用于下线通知
        Provider::ptr GetProvider(const BaseConnection::ptr &conn)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hashconns.find(conn);
            if (it != _hashconns.end())
                return it->second;
            else
                return Provider::ptr();
        }
        // 服务提供者连接断开时,删除hash中的相关信息，所以是根据连接删的
        void DelProvider(const BaseConnection::ptr &conn)
        {
            Provider::ptr provider;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                // 1.找到并删除_hashconns里的关联关系
                auto it = _hashconns.find(conn);
                if (it != _hashconns.end())
                {
                    provider = it->second;
                    _hashconns.erase(it);
                }
                else // 当前断开连接的，不是一个服务提供者
                    return;
                // 2.这个服务提供者走了，它里面的method都不会再提供，所以在_hashproviders中，对应的method的对应set里，删除走了的服务提供者
                for (auto &method : provider->_methods)
                {
                    auto &providers = _hashproviders[method];
                    providers.erase(provider);
                }
            }
        }
        //获取主机信息
        std::vector<Address> GetHosts(const std::string& method)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hashproviders.find(method);
            if(it == _hashproviders.end())
                return std::vector<Address>();
            std::vector<Address> hosts(it->second.begin(),it->second.end());
            return hosts;
        }

    private:
        std::mutex _mutex;
        std::unordered_map<std::string, std::set<Provider::ptr>> _hashproviders;
        std::unordered_map<BaseConnection::ptr, Provider::ptr> _hashconns;
    };

    class DiscovererManager
    {
    public:
        using ptr = std::shared_ptr<DiscovererManager>;
        struct Discoverer
        {
            using ptr = std::shared_ptr<Discoverer>;
            std::mutex _mutex;
            BaseConnection::ptr _conn;         // 发现者关联的客户端连接
            std::vector<std::string> _methods; // 发现过的服务名称
            Discoverer(const BaseConnection::ptr &conn)
                : _conn(conn)
            {
            }
            void AppendMethod(const std::string &method) // 添加发现的服务
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _methods.push_back(method);
            }
        };
        // 客户端进行服务发现时，新增发现者，新增服务名称
        Discoverer::ptr AddDiscoverer(const BaseConnection::ptr &conn, const std::string &method)
        {
            Discoverer::ptr discoverer;
            // 1.新增对应服务的发现者
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _hashconns.find(conn);
                if (it != _hashconns.end())
                    discoverer = it->second;
                else
                {
                    discoverer = std::make_shared<Discoverer>(conn);
                    _hashconns[conn] = discoverer;
                }
                // 2.把发现者要发现的服务和他自己的关联关系放进_hashdiscoverers
                auto &providers = _hashdiscoverers[method];
                providers.insert(discoverer);
            }
            // 给对应发现者新增一个发现过的服务
            discoverer->AppendMethod(method);
        }
        // 这里不用Get，因为客户端下线就下线了，DelDiscoverer之后，信息被删除，就不再通知了
        //  发现者客户端断开时,获取它的信息，删除hash中的相关信息，所以是根据连接删的
        void DelDiscoverer(const BaseConnection::ptr &conn)
        {
            Discoverer::ptr discoverer;
            // 1.查找要删除的这个连接对应的发现者
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _hashconns.find(conn);
                if (it == _hashconns.end())
                    return;
                // 查找到了，在_hashconns里删除
                else
                {
                    discoverer = it->second;
                    _hashconns.erase(it);
                }
                // 2.删除对应服务中的发现者，不再为该即将离开的发现者建立和它发现过的服务的关系
                for (auto &method : discoverer->_methods)
                {
                    auto &discoverers = _hashdiscoverers[method];
                    discoverers.erase(discoverer);
                }
            }
        }
        // 谁发现过这个服务，就通知发现者，又上线了一个服务者（上线通知）
        void OnlineNotify(const std::string &method, const Address &host, ServiceOptype optype)
        {
            return Notify(method,host,ns_fields::ServiceOptype::SERVICE_ONLINE);
        }
        // 谁发现过这个服务，就通知发现者，走了一个服务者（下线通知）
        void OfflineNotify(const std::string &method, const Address &host, ServiceOptype optype)
        {
            return Notify(method,host,ns_fields::ServiceOptype::SERVICE_OFFLINE);
        }

    private:
        void Notify(const std::string &method, const Address &host, ServiceOptype optype)
        {
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _hashdiscoverers.find(method);
                if (it == _hashdiscoverers.end())
                    return;
                auto msg_req = MessageFactory::Createmsg<ServiceRequest>();
                msg_req->Setid(ns_uuid::UUIDUtil::uuid());
                msg_req->SetMethod(method);
                msg_req->SetMtype(ns_fields::Mtype::SERVICE_REQ);
                msg_req->SetOtype(optype);
                msg_req->SetHost(host);
                for (auto &discover : it->second)
                {
                    discover->_conn->Send(msg_req);
                }
            }
        }
        std::mutex _mutex;
        std::unordered_map<std::string, std::set<Discoverer::ptr>> _hashdiscoverers;
        std::unordered_map<BaseConnection::ptr, Discoverer::ptr> _hashconns;
    };

    class PDManager
    {
    public:
        using ptr = std::shared_ptr<PDManager>;
        PDManager() 
        :_providers(std::make_shared<ProviderManager>())
        ,_discoverers(std::make_shared<DiscovererManager>())
        {}
        void OnServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
        {
            //这是发送过来的服务请求，先判断服务类型
            ServiceOptype otype = msg->GetOtype();
            if(otype == ServiceOptype::SERVICE_REGISTRY)
            {
                //服务注册:
                //1.新增服务提供者   2.通知服务上线
                _providers->AddProvider(conn,msg->GetHost(),msg->GetMethod());
                _discoverers->OnlineNotify(msg->GetMethod(),msg->GetHost(),ServiceOptype::SERVICE_ONLINE);
                return ProviderResponse(conn,msg);
            }
            else if(otype == ServiceOptype::SERVICE_DISCOVER)
            {
                //服务发现：
                //1.新增服务发现者
                _discoverers->AddDiscoverer(conn,msg->GetMethod());
                return DiscoverResponse(conn,msg);
            }
            else
            {
                LOG(ERROR,"收到服务请求，但操作类型错误");
                return ErrorResponse(conn,msg);
            }
        }
        void OnShutDown(const BaseConnection::ptr &conn)
        {
            //这是发过来的主机下线消息
            //是provider/discoverer都要删除对应关系
            auto it = _providers->GetProvider(conn);
            if(it.get() != nullptr)
            {
                _providers->DelProvider(conn);
            }
            //删discoverers不用判断，因为内部有判断了
            _discoverers->DelDiscoverer(conn);

        }

    private:    
        void ProviderResponse(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg)
        {
            auto msg_rsp = MessageFactory::Createmsg<ServiceResponse>();
            msg_rsp->Setid(msg->Getid());
            msg_rsp->SetMethod(msg->GetMethod());
            msg_rsp->SetMtype(Mtype::SERVICE_RES);
            msg_rsp->SetOtype(ServiceOptype::SERVICE_REGISTRY);
            msg_rsp->SetRcode(Rcode::RCODE_OK);
            conn->Send(msg_rsp);
        }
        void DiscoverResponse(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg)
        {
            std::vector<ns_msg::Address> hosts = _providers->GetHosts(msg->GetMethod());
            auto msg_rsp = MessageFactory::Createmsg<ServiceResponse>();
            msg_rsp->Setid(msg->Getid());
            msg_rsp->SetMtype(Mtype::SERVICE_RES);
            msg_rsp->SetOtype(ServiceOptype::SERVICE_REGISTRY);
            if(hosts.empty())
            {
                msg_rsp->SetRcode(Rcode::RCODE_NOT_FOUND_SERVICE);
                return conn->Send(msg_rsp);
            }
            msg_rsp->SetMethod(msg->GetMethod());
            msg_rsp->SetRcode(Rcode::RCODE_OK);
            msg_rsp->SetHosts(hosts);
            conn->Send(msg_rsp);
        }
        void ErrorResponse(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg)
        {
            auto msg_rsp = MessageFactory::Createmsg<ServiceResponse>();
            msg_rsp->Setid(msg->Getid());
            msg_rsp->SetMtype(Mtype::SERVICE_RES);
            msg_rsp->SetOtype(ServiceOptype::SERVICE_UNKNOW);
            msg_rsp->SetMethod(msg->GetMethod());
            msg_rsp->SetRcode(Rcode::RCODE_INVALID_OPTYPE);
            conn->Send(msg_rsp);
        }
        ProviderManager::ptr _providers;
        DiscovererManager::ptr _discoverers;
    };
}